import gainFreq
import clipNghbr
import myTools
import re
import jieba
from numpy import *

# "混合选择+剪辑近邻"使用的筛选文件
fileClipFrq = '剪辑后的词频.txt'

# "分开选择"使用的筛选文件
fileFrqPstv = '积极样本原始词频.txt'
fileFrqNgtv = '消极样本原始词频.txt'

# 构建新词集使用的文件,即上两种方式得到的词(多)集文件
fileKey = '不断增长的关键词.txt'
filePstv = '口红好评.txt'
fileNgtv = '口红差评.txt'
fileNewFrqPstv = '积极样本新词频.txt'
fileNewFrqNgtv = '消极样本新词频.txt'
plusNum = 7  # 测试时使用的增量值
fileTestPstv = '测试集(好评).txt'
fileTestNgtv = '测试集(差评).txt'


# kNN按特征选择出的特征训练得词频向量阵
def kNNTrain():
	global newSetLst
	global newSet
	# 获取新的有序词集
	with open(fileKey, 'r', encoding='utf-8') as f:
		newSetLst = re.split('\n|\\ufeff', f.read())  # 用正则去掉win下记事本的前缀
		newSet = set(newSetLst) - set([''])  # 新的词集
		newSetLst = list(newSet)  # 变成有序词集
		print("[+]新的有序词集:")
		print(newSetLst)
	# 初始化样本词频集与对应的标签向量
	global dataMatLst
	dataMatLst = []
	global labelVec
	labelVec = []
	# 积极样本分词并[按新的有序词集]转换成词频向量
	with open(filePstv, 'r', encoding='utf-8') as f1:
		with open(fileNewFrqPstv, 'w+', encoding='utf-8') as f2:
			f2.truncate()
			for line in f1.readlines()[0:300]:
				allZero = True  # 用这个量查除全是0的样本
				cutLst = jieba.cut(line)
				frqVec = [0 for i in range(len(newSetLst))]
				for cutWord in cutLst:
					# 如果在词集列表中
					if cutWord in newSet:
						# 对应位置加plusNum,使用词袋模型
						frqVec[newSetLst.index(cutWord)] += plusNum
						allZero = False  # 记录这个样本的词频向量并非全是0
				# 对这个样本的所有词循环结束后,判定不是全0向量才写入
				if not allZero:
					dataMatLst.append(frqVec)
					labelVec.append(1)
					# 数字转换成字符串
					frqVec = [str(i) for i in frqVec]
					# 用空格分隔开并写入文件
					f2.write(" ".join(frqVec) + "\n")
	# 消极样本分词并[按新的有序词集]转换成词频向量
	with open(fileNgtv, 'r', encoding='utf-8') as f1:
		with open(fileNewFrqNgtv, 'w+', encoding='utf-8') as f2:
			f2.truncate()
			for line in f1.readlines()[0:300]:
				allZero = True  # 用这个量查除全是0的样本
				cutLst = jieba.cut(line)
				frqVec = [0 for i in range(len(newSetLst))]
				for cutWord in cutLst:
					# 如果在词集列表中
					if cutWord in newSet:
						# 对应位置加plusNum,使用词袋模型
						frqVec[newSetLst.index(cutWord)] += plusNum
						allZero = False  # 记录这个样本的词频向量并非全是0
				# 对这个样本的所有词循环结束后,判定不是全0向量才写入
				if not allZero:
					dataMatLst.append(frqVec)
					labelVec.append(0)
					# 数字转换成字符串
					frqVec = [str(i) for i in frqVec]
					# 用空格分隔开并写入文件
					f2.write(" ".join(frqVec) + "\n")


# 用测试集作kNN测试
def kNNTest():
	global newSetLst
	global dataMatLst
	global labelVec
	errorNum = 0  # 记录分类错误的次数
	examLst = []  # 存考试的样本词频向量
	examVec = []  # 存考试的样本标签
	# 读入好评的考试样本
	with open(fileTestPstv, 'r', encoding='utf-8') as f:
		for line in f.readlines():
			frqVec = [0 for i in range(len(newSetLst))]
			cutLst = jieba.cut(line)
			for cutWord in cutLst:
				# 如果在词集列表中
				if cutWord in newSet:
					# 对应位置加plusNum,使用词袋模型
					frqVec[newSetLst.index(cutWord)] += plusNum
			examLst.append(frqVec)
			examVec.append(1)
	# 读入差评的考试样本
	with open(fileTestNgtv, 'r', encoding='utf-8') as f:
		for line in f.readlines():
			frqVec = [0 for i in range(len(newSetLst))]
			cutLst = jieba.cut(line)
			for cutWord in cutLst:
				# 如果在词集列表中
				if cutWord in newSet:
					# 对应位置加plusNum,使用词袋模型
					frqVec[newSetLst.index(cutWord)] += plusNum
			examLst.append(frqVec)
			examVec.append(0)
	# 测试:对于每个待考试的考试集样本(下标)
	for i in range(len(examLst)):
		# 重复成和新样本集一样多的矩阵
		diffMat = tile(examLst[i], (len(dataMatLst), 1))
		# 和新样本集相减得偏差矩阵
		diffMat = diffMat - mat(dataMatLst)

		# 使用L2范数:矩阵内每个元素平方
		sqDiffMat = multiply(diffMat, diffMat)

		# 行内求和,即得到和对应的临时测试集样本点的L2范数平方
		sqDstncMat = sqDiffMat.sum(axis=1)
		# mat变list
		sqDstncLst = [sqDstncMat[k, 0] for k in range(len(dataMatLst))]
		# list变array
		sqDstncArry = array(sqDstncLst)
		# 获取排序下标
		sortIdxArry = sqDstncArry.argsort()
		# 取前myK个最近的去判断(保证是奇数)
		myK = 5
		votePstv = 0
		voteNgtv = 0
		for j in range(myK):
			# 第i近临时测试集实例的标签
			if labelVec[sortIdxArry[j]] == 1:
				votePstv += 1
			elif labelVec[sortIdxArry[j]] == 0:
				voteNgtv += 1
		# 投票结束,判断投票(预测)结果
		###print('[%d,%d]'%(votePstv,voteNgtv),end='')
		tstRslt = (1 if votePstv >= voteNgtv else 0)
		###print('%d,'%tstRslt,end='')
		# 和真实标签比较,错误时,错误样本数+1
		if tstRslt != labelVec[i]:
			errorNum += 1
	print("[错误数目/测试总数]:%d/%d"%(errorNum,len(examLst)))
	print("[测试错误率]:%lf" % (errorNum / len(examLst)))


# 程序入口
if __name__ == "__main__":
	'''
	# (混合选择+剪辑近邻/分开选择):循环选择的起始
	gainFreq.cutWord()  # 分词并保存有序词集(与停用词有关)
	gainFreq.getFrq()  # 获取积极样本和消极样本的词频向量(与停用词有关)

	clipNghbr.readFrqLst()  # 从文件读入样本集的词频向量列表
	
	clipNghbr.clipNeighbor()  # 剪辑近邻

	myTools.getKeyWords(fileClipFrq)  # 筛选出那些可能的关键词看看(停用词集会变化)
	
	myTools.getKeyWords(fileFrqPstv) # 筛选出那些可能的关键词看看(停用词集会变化)
	myTools.getKeyWords(fileFrqNgtv)
	'''
	# 至此,一次循环选择的结束
	# kNN按特征选择出的特征训练得词频向量阵
	kNNTrain()
	# 用kNN做测试
	kNNTest()
